-- ECE 251 Team B Transit Subsystem
-- This class defines the Finite State Machine control
-- March 3, 2007

LIBRARY ieee;
USE ieee.std_logic_1164.all;
Use work.transmit_package.all;

ENTITY transmitFSM IS
	PORT
	(
		Clock								: IN	STD_LOGIC; -- Clock signal
		Queue_isEmpty						: IN	STD_LOGIC; -- Indicates that frame pointer queue is empty
															   -- Sit in idle state until not empty
		Queue_Trans							: IN	STD_LOGIC;  -- Transmission signal from FPQ
															    -- Goes high one clock cycle before data
																-- Goes low one clock cycle before end
		Queue_FP							: IN	STD_LOGIC_VECTOR(8 DOWNTO 0); -- Frame Pointer Transmission Data
		Queue_VLAN_Mem						: IN	STD_LOGIC; -- Bit that indicates VLAN membership if set
		Queue_VLAN_Tag						: IN	STD_LOGIC_VECTOR(31 DOWNTO 0); -- VLAN membership tag
		Mem_DataValid						: IN	STD_LOGIC; -- Interrupt signal from memory indicating incoming data
															   -- High one clock cycle before start of data
		Mem_Data							: IN	STD_LOGIC_VECTOR(15 DOWNTO 0); -- Incoming data from memory in 16-bit blocks
		Mem_TransDone						: IN	STD_LOGIC; -- Single clock cycle signal indicating end of one frame transmission
		
		FSM_GetNext							: OUT	STD_LOGIC; -- Sends signal to FPQ requesting a new data pointer
		FSM_MemSend							: OUT	STD_LOGIC; -- Signal indicating outgoing data to memory
		FSM_MemData							: OUT	STD_LOGIC_VECTOR(8 DOWNTO 0); -- Data going to memory

		FSM_FrameASend						: OUT	STD_LOGIC; -- Signal indicating data going to frame buffer
		FSM_FrameAData_0					: OUT	STD_LOGIC_VECTOR(3 DOWNTO 0); -- Data going to frame buffer
		FSM_FrameAData_1					: OUT	STD_LOGIC_VECTOR(3 DOWNTO 0); -- Data going to frame buffer
		FSM_FrameAData_2					: OUT	STD_LOGIC_VECTOR(3 DOWNTO 0); -- Data going to frame buffer
		FSM_FrameAData_3					: OUT	STD_LOGIC_VECTOR(3 DOWNTO 0); -- Data going to frame buffer
		FSM_FrameAComplete					: OUT	STD_LOGIC; -- Signal indicating frame complete
		FSM_FrameAOdd						: OUT 	STD_LOGIC;
		FB_BufferAFull						: IN	STD_LOGIC; -- Frame buffers are full
		
		FSM_FrameBSend						: OUT	STD_LOGIC; -- Signal indicating data going to frame buffer
		FSM_FrameBData_0					: OUT	STD_LOGIC_VECTOR(3 DOWNTO 0); -- Data going to frame buffer
		FSM_FrameBData_1					: OUT	STD_LOGIC_VECTOR(3 DOWNTO 0); -- Data going to frame buffer
		FSM_FrameBData_2					: OUT	STD_LOGIC_VECTOR(3 DOWNTO 0); -- Data going to frame buffer
		FSM_FrameBData_3					: OUT	STD_LOGIC_VECTOR(3 DOWNTO 0); -- Data going to frame buffer
		FSM_FrameBComplete					: OUT	STD_LOGIC; -- Signal indicating frame complete
		FSM_FrameBOdd						: OUT	STD_LOGIC;
		FB_BufferBFull						: IN	STD_LOGIC; -- Frame buffers are full
	
	    Init								: OUT	STD_LOGIC;
	
		VLAN_Mem							: BUFFER	STD_LOGIC;
		VLAN_Tag							: BUFFER	STD_LOGIC_VECTOR(31 DOWNTO 0)
	);
END transmitFSM;

ARCHITECTURE Behavior OF transmitFSM IS
	TYPE state_type IS (INITIALIZE, RESET, fpREQUEST, fpSEND, dataRequest1, dataSend1, Qframe, Qwait, Qframe2, Qwait2, length, Lwait, dataRequest2, dataSend2, transDONE);
	SIGNAL state: state_type := INITIALIZE;
	
	Signal putInA: Std_logic;
	
	Signal request1Count	: Integer;
	
	Signal isOdd			: Std_logic;
	
	Signal initCount		: Integer := 0;
BEGIN
	PROCESS (Clock)
	BEGIN
		IF Clock'EVENT AND Clock = '1' THEN
			CASE state IS
				WHEN INITIALIZE =>
					Init <= '1';
					If initCount = 5 Then
						state <= RESET;
					Else
						state <= INITIALIZE;
						initCount <= initCount + 1;
					End If;
				WHEN RESET => -- Reset State
					IF ((FB_BufferAFull = '1') AND (FB_BufferBFull = '1')) THEN
						state <= RESET;
					ELSIF Queue_isEmpty = '0' THEN
						state <= fpREQUEST;
						FSM_GetNext <= '1';
					Else
						state <= RESET;
					END IF;
					Init <= '0';
					isOdd <= '0';
				WHEN fpREQUEST => -- State for requesting a frame pointer from the buffer.
					FSM_GetNext <= '0';
					IF Queue_Trans = '1' THEN
						FSM_MemSend <= '1';
						state <= fpSEND;
					END IF;
				WHEN fpSEND => -- Forward the frame pointer as it receives it from the buffer to the shared memory.
					FSM_MemData <= Queue_FP;
					VLAN_Mem <= Queue_VLAN_Mem;
					VLAN_Tag <= Queue_VLAN_Tag;
					IF Queue_Trans = '0' THEN
						FSM_MemSend <= '0';
						IF FB_BufferAFull = '0' THEN
							putInA <= '1';
						ELSE
							putInA <= '0';
						END IF;
						state <= dataRequest1;
						request1Count <= 0;
					END IF;
				WHEN dataRequest1 => -- Frame pointer forwarding complete, awaiting new frame from memory.
					If putInA = '1' Then
						FSM_FrameAData_0 <= "0000";
						FSM_FrameAData_1 <= "0000";
						FSM_FrameAData_2 <= "0000";
						FSM_FrameAData_3 <= "0000";
						FSM_MemData <= "000000000";
					Else
						FSM_FrameBData_0 <= "0000";
						FSM_FrameBData_1 <= "0000";
						FSM_FrameBData_2 <= "0000";
						FSM_FrameBData_3 <= "0000";
						FSM_MemData <= "000000000";
					End If;
					
					If Mem_DataValid = '1' Then
						state <= dataSend1;
						request1Count <=  request1Count + 1;
						If putInA = '1' Then
							FSM_FrameAsend <= '1';
						Else
							FSM_FrameBsend <= '1';
						End If;
					Else
						state <= dataRequest1;
					End If;
				When dataSend1 =>
					If putInA = '1' Then
						FSM_FrameAData_0 <= Mem_Data(3 DOWNTO 0);
						FSM_FrameAData_1 <= Mem_Data(7 DOWNTO 4);
						FSM_FrameAData_2 <= Mem_Data(11 DOWNTO 8);
						FSM_FrameAData_3 <= Mem_Data(15 DOWNTO 12);
					Else
						FSM_FrameBData_0 <= Mem_Data(3 DOWNTO 0);
						FSM_FrameBData_1 <= Mem_Data(7 DOWNTO 4);
						FSM_FrameBData_2 <= Mem_Data(11 DOWNTO 8);
						FSM_FrameBData_3 <= Mem_Data(15 DOWNTO 12);
					End If;
					
					If request1Count = 6 Then
						If Mem_DataValid = '1' Then
							state <= Qframe;
							If putInA = '1' Then
								FSM_FrameAsend <= '1';
							Else
								FSM_FrameBsend <= '1';
							End If;
						Else
							state <= Qwait;
							FSM_FrameAsend <= '0';
							FSM_FrameBsend <= '0';
						End If;
					Else
						If Mem_DataValid = '1' Then
							state <= dataSend1;
							request1Count <= request1Count + 1;
						Else
							state <= dataRequest1;
							FSM_FrameAsend <= '0';
							FSM_FrameBsend <= '0';
						End If;
					End If;
				When Qframe =>
					If Mem_Data >= "0000011000000000" Then
						If VLAN_Mem = '1' Then -- It's VLAN and we have to replace it
							If putInA = '1' Then
								FSM_FrameAData_0 <= VLAN_Tag(3 DOWNTO 0);
								FSM_FrameAData_1 <= VLAN_Tag(7 DOWNTO 4);
								FSM_FrameAData_2 <= VLAN_Tag(11 DOWNTO 8);
								FSM_FrameAData_3 <= VLAN_Tag(15 DOWNTO 12);
							Else
								FSM_FrameBData_0 <= VLAN_Tag(3 DOWNTO 0);
								FSM_FrameBData_1 <= VLAN_Tag(7 DOWNTO 4);
								FSM_FrameBData_2 <= VLAN_Tag(11 DOWNTO 8);
								FSM_FrameBData_3 <= VLAN_Tag(15 DOWNTO 12);
							End If;
							state <= Qframe2;
							If putInA = '1' Then
								FSM_FrameAsend <= '1';
							Else
								FSM_frameBsend <= '1';
							End If;
						Else  --It's VLAN but we don't have to replace it
							If putInA = '1' Then
								FSM_FrameAData_0 <= Mem_Data(3 DOWNTO 0);
								FSM_FrameAData_1 <= Mem_Data(7 DOWNTO 4);
								FSM_FrameAData_2 <= Mem_Data(11 DOWNTO 8);
								FSM_FrameAData_3 <= Mem_Data(15 DOWNTO 12);
							Else
								FSM_FrameBData_0 <= Mem_Data(3 DOWNTO 0);
								FSM_FrameBData_1 <= Mem_Data(7 DOWNTO 4);
								FSM_FrameBData_2 <= Mem_Data(11 DOWNTO 8);
								FSM_FrameBData_3 <= Mem_Data(15 DOWNTO 12);
							End If;
							If Mem_DataValid = '1' Then
								state <= Qframe2;
								If putInA = '1' Then
									FSM_FrameAsend <= '1';
								Else
									FSM_frameBsend <= '1';
								End If;
							Else
								state <= Qwait2;
								FSM_FrameAsend <= '0';
								FSM_FrameBsend <= '0';
							End If;
						End If;
					Else -- It's not VLAN, get size
						If putInA = '1' Then
							FSM_FrameAData_0 <= Mem_Data(3 DOWNTO 0);
							FSM_FrameAData_1 <= Mem_Data(7 DOWNTO 4);
							FSM_FrameAData_2 <= Mem_Data(11 DOWNTO 8);
							FSM_FrameAData_3 <= Mem_Data(15 DOWNTO 12);
						Else
							FSM_FrameBData_0 <= Mem_Data(3 DOWNTO 0);
							FSM_FrameBData_1 <= Mem_Data(7 DOWNTO 4);
							FSM_FrameBData_2 <= Mem_Data(11 DOWNTO 8);
							FSM_FrameBData_3 <= Mem_Data(15 DOWNTO 12);
						End If;
						isOdd <= Mem_Data(0);
						If Mem_DataValid = '1' Then
							state <= dataSend2;
							If putInA = '1' Then
								FSM_FrameAsend <= '1';
							Else
								FSM_frameBsend <= '1';
							End If;
						Else
							state <= dataRequest2;
							FSM_FrameAsend <= '0';
							FSM_FrameBsend <= '0';
						End If;
					End If;
				When Qwait =>
					If putInA = '1' Then
						FSM_FrameAData_0 <= "0000";
						FSM_FrameAData_1 <= "0000";
						FSM_FrameAData_2 <= "0000";
						FSM_FrameAData_3 <= "0000";
						FSM_MemData <= "000000000";
					Else
						FSM_FrameBData_0 <= "0000";
						FSM_FrameBData_1 <= "0000";
						FSM_FrameBData_2 <= "0000";
						FSM_FrameBData_3 <= "0000";
						FSM_MemData <= "000000000";
					End If;
					
					If Mem_DataValid = '1' Then
						state <= Qframe;
						If putInA = '1' Then
							FSM_FrameAsend <= '1';
						Else
							FSM_FrameBsend <= '1';
						End If;
					Else
						state <= Qwait;
					End If;
				When Qframe2 =>
					If VLAN_Mem = '1' Then -- It's VLAN and we have to replace it
						If putInA = '1' Then
							FSM_FrameAData_0 <= VLAN_Tag(19 DOWNTO 16);
							FSM_FrameAData_1 <= VLAN_Tag(23 DOWNTO 20);
							FSM_FrameAData_2 <= VLAN_Tag(27 DOWNTO 24);
							FSM_FrameAData_3 <= VLAN_Tag(31 DOWNTO 28);
						Else
							FSM_FrameBData_0 <= VLAN_Tag(19 DOWNTO 16);
							FSM_FrameBData_1 <= VLAN_Tag(23 DOWNTO 20);
							FSM_FrameBData_2 <= VLAN_Tag(27 DOWNTO 24);
							FSM_FrameBData_3 <= VLAN_Tag(31 DOWNTO 28);
						End If;
					Else  --It's VLAN but we don't have to replace it
						If putInA = '1' Then
							FSM_FrameAData_0 <= Mem_Data(3 DOWNTO 0);
							FSM_FrameAData_1 <= Mem_Data(7 DOWNTO 4);
							FSM_FrameAData_2 <= Mem_Data(11 DOWNTO 8);
							FSM_FrameAData_3 <= Mem_Data(15 DOWNTO 12);
						Else
							FSM_FrameBData_0 <= Mem_Data(3 DOWNTO 0);
							FSM_FrameBData_1 <= Mem_Data(7 DOWNTO 4);
							FSM_FrameBData_2 <= Mem_Data(11 DOWNTO 8);
							FSM_FrameBData_3 <= Mem_Data(15 DOWNTO 12);
						End If;
					End If;
					
					If Mem_DataValid = '1' Then
						state <= length;
						If putInA = '1' Then
							FSM_FrameAsend <= '1';
						Else
							FSM_frameBsend <= '1';
						End If;
					Else
						state <= Lwait;
						FSM_FrameAsend <= '0';
						FSM_FrameBsend <= '0';
					End If;
				When Qwait2 =>
					If putInA = '1' Then
						FSM_FrameAData_0 <= "0000";
						FSM_FrameAData_1 <= "0000";
						FSM_FrameAData_2 <= "0000";
						FSM_FrameAData_3 <= "0000";
						FSM_MemData <= "000000000";
					Else
						FSM_FrameBData_0 <= "0000";
						FSM_FrameBData_1 <= "0000";
						FSM_FrameBData_2 <= "0000";
						FSM_FrameBData_3 <= "0000";
						FSM_MemData <= "000000000";
					End If;
					
					If Mem_DataValid = '1' Then
						state <= Qframe2;
						If putInA = '1' Then
							FSM_FrameAsend <= '1';
						Else
							FSM_FrameBsend <= '1';
						End If;
					Else
						state <= Qwait2;
					End If;
				When length =>
					isOdd <= Mem_Data(0);
					If putInA = '1' Then
						FSM_FrameAData_0 <= Mem_Data(3 DOWNTO 0);
						FSM_FrameAData_1 <= Mem_Data(7 DOWNTO 4);
						FSM_FrameAData_2 <= Mem_Data(11 DOWNTO 8);
						FSM_FrameAData_3 <= Mem_Data(15 DOWNTO 12);
					Else
						FSM_FrameBData_0 <= Mem_Data(3 DOWNTO 0);
						FSM_FrameBData_1 <= Mem_Data(7 DOWNTO 4);
						FSM_FrameBData_2 <= Mem_Data(11 DOWNTO 8);
						FSM_FrameBData_3 <= Mem_Data(15 DOWNTO 12);
					End If;
					If Mem_DataValid = '1' Then
						state <= dataSend2;
						If putInA = '1' Then
							FSM_FrameAsend <= '1';
						Else
							FSM_frameBsend <= '1';
						End If;
					Else
						state <= dataRequest2;
						FSM_FrameAsend <= '0';
						FSM_FrameBsend <= '0';
					End If;
				When Lwait =>
					If putInA = '1' Then
						FSM_FrameAData_0 <= "0000";
						FSM_FrameAData_1 <= "0000";
						FSM_FrameAData_2 <= "0000";
						FSM_FrameAData_3 <= "0000";
						FSM_MemData <= "000000000";
					Else
						FSM_FrameBData_0 <= "0000";
						FSM_FrameBData_1 <= "0000";
						FSM_FrameBData_2 <= "0000";
						FSM_FrameBData_3 <= "0000";
						FSM_MemData <= "000000000";
					End If;
					
					If Mem_DataValid = '1' Then
						state <= length;
						If putInA = '1' Then
							FSM_FrameAsend <= '1';
						Else
							FSM_FrameBsend <= '1';
						End If;
					Else
						state <= Lwait;
					End If;
				When dataRequest2 =>
					If putInA = '1' Then
						FSM_FrameAData_0 <= "0000";
						FSM_FrameAData_1 <= "0000";
						FSM_FrameAData_2 <= "0000";
						FSM_FrameAData_3 <= "0000";
						FSM_MemData <= "000000000";
					Else
						FSM_FrameBData_0 <= "0000";
						FSM_FrameBData_1 <= "0000";
						FSM_FrameBData_2 <= "0000";
						FSM_FrameBData_3 <= "0000";
						FSM_MemData <= "000000000";
					End If;
					
					If Mem_DataValid = '1' Then
						state <= dataSend2;
						If putInA = '1' Then
							FSM_FrameAsend <= '1';
						Else
							FSM_FrameBsend <= '1';
						End If;
					Else
						state <= dataRequest2;
					End If;
				When dataSend2 =>
					If putInA = '1' Then
						FSM_FrameAData_0 <= Mem_Data(3 DOWNTO 0);
						FSM_FrameAData_1 <= Mem_Data(7 DOWNTO 4);
						FSM_FrameAData_2 <= Mem_Data(11 DOWNTO 8);
						FSM_FrameAData_3 <= Mem_Data(15 DOWNTO 12);
					Else
						FSM_FrameBData_0 <= Mem_Data(3 DOWNTO 0);
						FSM_FrameBData_1 <= Mem_Data(7 DOWNTO 4);
						FSM_FrameBData_2 <= Mem_Data(11 DOWNTO 8);
						FSM_FrameBData_3 <= Mem_Data(15 DOWNTO 12);
					End If;
					
					If Mem_DataValid = '1' Then
						state <= dataSend2;
						If putInA = '1' Then
							FSM_FrameAsend <= '1';
						Else
							FSM_FrameBsend <= '1';
						End If;
					Else
						If Mem_TransDone = '0' Then
							state <= dataRequest2;
						Else
							state <= transDONE;
							If putInA = '1' Then
								FSM_FrameAComplete <= '1';
							Else
								FSM_FrameBComplete <= '1';
							End If;
							
							If isOdd = '1' Then
								If putInA = '1' Then
									FSM_FrameAOdd <= '1';
								Else
									FSM_FrameBOdd <= '1';
								End If;
							Else
								FSM_FrameAOdd <= '0';
								FSM_FrameBOdd <= '0';
							End If;							
						End If;
						FSM_FrameAsend <= '0';
						FSM_FrameBsend <= '0';
					End If;
				WHEN transDONE => -- Finished transmission
					FSM_FrameAData_0 <= "0000";
					FSM_FrameAData_1 <= "0000";
					FSM_FrameAData_2 <= "0000";
					FSM_FrameAData_3 <= "0000";
					FSM_FrameAComplete <= '0';
					FSM_FrameBData_0 <= "0000";
					FSM_FrameBData_1 <= "0000";
					FSM_FrameBData_2 <= "0000";
					FSM_FrameBData_3 <= "0000";
					FSM_FrameBComplete <= '0';
					FSM_FrameAOdd <= '0';
					FSM_FrameBOdd <= '0';
					state <= RESET;
			END CASE;
		END IF;
	END PROCESS;

END Behavior;
